package ren.yugong.fileutil;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

public class TxtLineObject {

    public static <T> String line(T t) {
        TxtTemplateModel<?> template = TxtTemplateFactory.getTemplate(t.getClass());
        String column = template.getColumnSplit();
        List<MethodInfo> infos = template.getWriteMethodInfos();
        StringBuffer sb = new StringBuffer();
        int size = infos.size();
        for (int i = 0; i < size; i++) {
            MethodInfo m = infos.get(i);
            String value = value(t, m);
            sb = sb.append(value);
            if (i != size-1) {
                sb.append(column);
            }
        }
        return sb.toString();
    }

    public static <T> T object(String line, Class<T> cls) {
        TxtTemplateModel<T> templateModel = TxtTemplateFactory.getTemplate(cls);

        String columnSplit = templateModel.getColumnSplit();
        List<MethodInfo> infos = templateModel.getReadMethodInfos();
        Class<T> txtTemplateCls = templateModel.getTxtTemplateCls();

        T t = null;
        try {
            t = txtTemplateCls.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

        if (infos.size() == 1) {
            MethodInfo methodInfo = infos.get(0);
            Method method = methodInfo.getMethod();
            Class<?> parameterType = method.getParameterTypes()[0];
            Object value = value(line, parameterType, methodInfo);
            try {
                method.invoke(t, value);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        } else {

            if (columnSplit.isEmpty()) {
                LengthType lengthType = templateModel.getLengthType();
                String charset = templateModel.getCharset();

                int beginIndex = 0;
                switch (lengthType) {
                    case CHAR:

                        for (MethodInfo info : infos) {
                            Method method = info.getMethod();
                            Class<?> parameterType = method.getParameterTypes()[0];

                            int length = info.getLength();
                            String s = line.substring(beginIndex, beginIndex + length);
                            Object value = value(s, parameterType, info);

                            try {
                                method.invoke(t, value);
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            } catch (InvocationTargetException e) {
                                e.printStackTrace();
                            }


                            beginIndex += length;

                        }
                        break;
                    default:
                        byte[] bytes = null;
                        try {
                            bytes = line.getBytes(charset);
                        } catch (UnsupportedEncodingException e) {
                            e.printStackTrace();
                        }

                        for (MethodInfo info : infos) {
                            Method method = info.getMethod();
                            Class<?> parameterType = method.getParameterTypes()[0];

                            int length = info.getLength();
                            byte[] bs = copy(bytes, beginIndex, beginIndex + length);
                            String s = null;
                            try {
                                s = new String(bs, charset);
                            } catch (UnsupportedEncodingException e) {
                                e.printStackTrace();
                            }
                            Object value = value(s, parameterType, info);

                            try {
                                method.invoke(t, value);
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            } catch (InvocationTargetException e) {
                                e.printStackTrace();
                            }


                            beginIndex += length;

                        }

                }

            } else {
                String[] strs = line.split(columnSplit, -1);

                if (strs.length != infos.size()) {
                    throw new TemplateCommonException("txt line columns size not equals template columns size");
                }

                try {

                    for (int i = 0; i < infos.size(); i++) {
                        MethodInfo mInfo = infos.get(i);
                        Method method = mInfo.getMethod();
                        Class<?> parameterType = method.getParameterTypes()[0];

                        String s = strs[i];
                        if (null == s) {
                            continue;
                        }

                        Object value = value(s, parameterType, mInfo);

                        method.invoke(t, value);

                    }

                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }

            }
        }

        return t;
    }

    private static byte[] copy(byte[] bytes, int beginIndex, int endingIndex) {
        byte[] a = new byte[endingIndex - beginIndex];
        for (int i = beginIndex, j = 0; i < endingIndex; i++,j++) {
            a[i] = bytes[i];
        }
        return a;
    }

    private static Object value(String s, Class<?> parameterType, MethodInfo mInfo) {
        if (parameterType.isPrimitive()) {
            if (int.class == parameterType) {
                return Integer.parseInt(s);
            }
        } else {
            if (String.class.isAssignableFrom(parameterType)) {
                return s;
            } else if (Date.class.isAssignableFrom(parameterType)) {
                String format = mInfo.getFormat();
                SimpleDateFormat sdf = new SimpleDateFormat(format);
                try {
                    return sdf.parse(s);
                } catch (ParseException e) {
                    e.printStackTrace();
                }

            }
        }
        return null;
    }

    private static <T> String value(T t, MethodInfo methodInfo) {
        String value;
        Method method = methodInfo.getMethod();
        int length = methodInfo.getLength();
        Object invoke;
        try {
            invoke = method.invoke(t);
        } catch (IllegalAccessException e) {
            throw new TemplateCommonException(e);
        } catch (InvocationTargetException e) {
            throw new TemplateCommonException(e);
        }

        if (invoke != null) {
            if (invoke instanceof Number) {
                value = invoke.toString();
            } else if (invoke instanceof String) {
                value = (String)invoke;
                value = format(value, length);
            } else if (invoke instanceof Date) {
                String format = methodInfo.getFormat();
                SimpleDateFormat sf = new SimpleDateFormat(format);
                value = sf.format(invoke);
            } else if (invoke instanceof Boolean){
                value = invoke.toString();
            } else {
                value = invoke.toString();
                value = format(value, length);
            }
        } else {
            value = "";
        }

        if (length == 0 || length >= value.length()) {
            return value;
        } else {
            throw new TemplateCommonException("column length < column value length");
        }

    }

    private static String format(String value, int l) {
        if (l == 0) {
            return value;
        }
        return String.format("%" + (-l) + "s", value);
    }

}
